package sim.app.geo.haiti;



import sim.engine.SimState;
import sim.engine.Steppable;
import sim.field.grid.IntGrid2D;
import sim.field.grid.ObjectGrid2D;
import sim.field.grid.SparseGrid2D;
import sim.util.Bag;



/**
 * Used to propagate information about food availability
 * <p/>
 */
public class RumorMill implements Steppable
{
    private static final long serialVersionUID = 1L;

    int gridWidth;

    int gridHeight;

    int rumorDist = 1;

    int neighborhoodType = 1; // 0 = von Neumann, 1 = Moore


    IntGrid2D assembleInfo(SparseGrid2D population)
    {
        IntGrid2D info = new IntGrid2D(gridWidth, gridHeight);

        // assemble info for each tile
        for (int i = 0; i < gridWidth; i++)
        {
            for (int j = 0; j < gridHeight; j++)
            {

                int tileinfo = 0;
                // comprehensive info for this tile

                // check with all agents
                Bag agents = population.getObjectsAtLocation(i, j);
                if (agents != null)
                {
                    for (Object o : agents)
                    {
                        Agent a = (Agent) o;
                        tileinfo = (a.centerInfo | tileinfo);
                    }
                }

                info.set(i, j, tileinfo);
            }
        }
        return info;
    }


    IntGrid2D assembleInfoFromNeighbors(IntGrid2D info, ObjectGrid2D locations)
    {

        IntGrid2D infoFromNeighbors = new IntGrid2D(gridWidth, gridHeight);

        for (int i = 0; i < gridWidth; i++)
        {
            for (int j = 0; j < gridHeight; j++)
            {

                // comprehensive info for this tile
                int tileinfo = 0;

                Bag neighbors = new Bag();
                if (neighborhoodType == 0)
                {
                    locations.getNeighborsHamiltonianDistance(i, j, rumorDist, false, neighbors, null, null);
                } else if (neighborhoodType == 1)
                {
                    locations.getNeighborsMaxDistance(i, j, rumorDist, false, neighbors, null, null);
                }

                for (Object o : neighbors)
                {
                    Location l = (Location) o;

                    if (l.x == i && l.y == j)
                    {
                        continue; // don't exchange info with self
                    }
                    // get the information from the neighbor tile
                    int infoNeighboringTile = info.get(l.x, l.y);
                    tileinfo = (tileinfo | infoNeighboringTile);

                }

                infoFromNeighbors.set(i, j, tileinfo);
            }
        }

        return infoFromNeighbors;
    }


    IntGrid2D distributeInfo(SparseGrid2D population, IntGrid2D oldInfo,
                             IntGrid2D neighborInfo)
    {

        // assemble info for each tile
        for (int i = 0; i < gridWidth; i++)
        {
            for (int j = 0; j < gridHeight; j++)
            {

                // comprehensive info for this tile
                int neighborInfoTile = neighborInfo.get(i, j);
                neighborInfoTile = (neighborInfoTile | oldInfo.get(i, j));

                Bag agents = population.getObjectsAtLocation(i, j);
                if (agents != null)
                {
                    for (Object o : agents)
                    {
                        ((Agent) o).centerInfo = neighborInfoTile;
                    }
                }
            }
        }
        return neighborInfo;
    }


    @Override
    public void step(SimState state)
    {

        SparseGrid2D population = ((HaitiFood) state).population;

        System.out.println("RUMOR: " + state.schedule.getSteps());

        // TODO: want a Moore vs von Neumann neighborhood? change it in these functions!
        System.out.println("assemble info...");
        IntGrid2D info = assembleInfo(population);
        System.out.println("info assembled");

        System.out.println("consult neighbors...");
        IntGrid2D infoFromNeighbors = assembleInfoFromNeighbors(info, ((HaitiFood) state).locations);
        System.out.println("neighbors consulted");

        System.out.println("redistribute info...");
        distributeInfo(population, info, infoFromNeighbors);
        System.out.println("info redistributed");

    }


}